# Software Design Document — RVC Controller SW v2

| 항목 | 내용 |
|------|------|
| 문서 번호 | RVC-SDD-002 |
| 버전 | v2.0 |
| 작성일 | 2026-05-29 |
| 작성자 | OOAD Team 9 |
| 상태 | Released |

---

## 변경 이력 (Revision History)

| 버전 | 날짜 | 변경 내용 | 작성자 |
|------|------|-----------|--------|
| v1.0 | 2026-05-20 | 최초 작성 | OOAD Team 9 |
| v2.0 | 2026-05-29 | HW 변경 반영 — 우측 장애물 센서 제거, v2 로직 구현, SimFrontSensor 추가 | OOAD Team 9 |

---

## v1 → v2 변경 요약 (Change Summary)

| 변경 구분 | 파일 / 클래스 | 변경 내용 |
|-----------|--------------|-----------|
| **[삭제]** | `SensorPosition::RIGHT` | 우측 HW 센서 제거에 따라 열거값 삭제 |
| **[변경]** | `Controller` 생성자 | `rightSensor*` 파라미터 제거 (6→5개) |
| **[변경]** | `Controller::checkSideObstacles()` | 좌측 센서만 직접 확인 |
| **[변경]** | `Controller::avoidFrontObstacle()` | 우측 확인: `turn(RIGHT)` + `frontSensor->readData()` + `isObstacleDetected()` |
| **[변경]** | `Controller::avoidAllDirectionObstacle()` | `turn(BACK)` 제거 → 후진 후 방향 재판단 (좌→우 순서) |
| **[삭제]** | `Controller::rightObstacle_` 멤버 | 우측 상태 필드 제거 |
| **[추가]** | `SimFrontSensor` | 방향 인식 전방 센서 — `isFacingRight()` 기반 이중 상태 반환 |
| **[변경]** | `SimMovementUnit::turn()` | `facingRight_` 플래그 추가; 다른 기동 시 초기화 |
| **[변경]** | `RVCSimulator` | `rightSensor_` → 제거; `frontSensor_`를 `SimFrontSensor`로 교체; 멤버 선언 순서 변경 |
| **[삭제]** | `RVCSimulator::setRightObstacle()` | 우측 HW 센서 없음 |
| **[추가]** | `RVCSimulator::setRightSideObstacle()` | `SimFrontSensor.setRightSideObstacle()` 위임 |
| **[변경]** | `simulator/main.cpp` | `right` 명령 → `rightside` 명령으로 교체 |
| **[삭제]** | `TC-UC02-002` 중 `SensorPosition::RIGHT` | 해당 enum 값 없음 |
| **[삭제]** | `TC-UC04-002` (reportObstacle RIGHT) | 우측 HW 센서 없음 |
| **[변경]** | `TC-UC03-002` | left blocked → `turn(RIGHT)` + front sensor 재조회로 우측 확인 |
| **[변경]** | `TC-UC03-003` | 전방향 막힘 판단 방식 변경 (front sensor 2회 조회) |
| **[변경]** | `TC-UC03-004` | 좌측 센서만 직접 조회 검증 |
| **[변경]** | `TC-UC05-001/002` | v2 시퀀스 반영 (turn이 backward 전후 모두 발생) |

---

## 1. 아키텍처 개요

### 1.1 4계층 구조 (v1과 동일)

```
┌──────────────────────────────────────────────────────────┐
│  Layer 4: Facade             RVC                         │
├──────────────────────────────────────────────────────────┤
│  Layer 3: Controller         Controller                  │
├──────────────────────────────────────────────────────────┤
│  Layer 2: HW Abstraction     ObstacleSensor  DustSensor  │
│  (Interface)                 MovementUnit    CleaningUnit│
├──────────────────────────────────────────────────────────┤
│  Layer 1: Concrete HW /      SimFrontSensor  SimObstacle │
│  Simulator                   SimMovement     SimCleaning │
└──────────────────────────────────────────────────────────┘
```

### 1.2 v2 핵심 설계 결정

**우측 장애물 감지 메커니즘 (DR-07):**
우측 HW 센서 없이 우측 방향을 감지하기 위해, Controller는 `MovementUnit::turn(Direction::RIGHT)`를 호출한 후 `frontSensor_->readData()` + `frontSensor_->isObstacleDetected()`를 재조회한다. 물리적으로 우측을 향하게 된 전방 센서가 우측 방향 장애물을 감지한다.

**SimFrontSensor 설계:**
실제 HW에서는 로봇이 우회전하면 전방 센서가 원래 우측을 향한다. 시뮬레이터에서는 `SimMovementUnit::isFacingRight()`를 확인하여, `turn(RIGHT)` 이후 `isObstacleDetected()` 호출 시 `rightSide_` 상태를 반환하도록 구현한다.

---

## 2. 주요 변경 클래스 상세

### 2.1 Controller (변경)

#### 생성자 [변경]
```cpp
// v1
Controller(MovementUnit*, CleaningUnit*,
           ObstacleSensor* front, ObstacleSensor* left, ObstacleSensor* right,
           DustSensor*);

// v2 [변경] rightSensor 파라미터 제거
Controller(MovementUnit*, CleaningUnit*,
           ObstacleSensor* front, ObstacleSensor* left,
           DustSensor*);
```

#### avoidFrontObstacle() [변경]
```cpp
// v1: checkSideObstacles() → left/right 둘 다 확인 → turn(free side) or escalate
// v2: checkSideObstacles() → left만 확인
//     if left free → turn(LEFT) → forward
//     else → turn(RIGHT) → frontSensor→readData() → isObstacleDetected()
//            → if clear: forward
//            → if blocked: avoidAllDirectionObstacle()
```

#### avoidAllDirectionObstacle() [변경]
```cpp
// v1: stop → backward → turn(BACK) → forward
// v2: stop → backward → checkSideObstacles(left)
//     → if left free: turn(LEFT) → forward
//     → else: turn(RIGHT) → frontSensor→readData() → forward
```

#### checkSideObstacles() [변경]
```cpp
// v1: leftSensor→readData() + rightSensor→readData() (양쪽 확인)
// v2: leftSensor→readData() 만 (우측은 avoidFrontObstacle에서 inline 처리)
```

#### recordObstacleDetection() [변경]
```cpp
// v1: FRONT / LEFT / RIGHT 세 케이스
// v2: FRONT / LEFT 두 케이스 (SensorPosition::RIGHT 삭제)
```

### 2.2 SimFrontSensor (추가)

`SimObstacleSensor`의 고정 상태 반환 방식을 대체한다. `SimMovementUnit`의 `isFacingRight()`를 실시간으로 조회하여 회전 방향에 따라 다른 장애물 상태를 반환한다.

```
turn(RIGHT) 호출          turn(LEFT/FRONT) 또는 moveForward() 호출
     │                              │
facingRight_ = true          facingRight_ = false
     │                              │
isObstacleDetected()         isObstacleDetected()
  → rightSide_                 → forward_
```

### 2.3 SimMovementUnit (변경)

`facingRight_` 불리언 플래그 추가:
- `turn(Direction::RIGHT)` 호출 시 `facingRight_ = true`
- `turn(LEFT/FRONT/BACK)`, `moveForward()`, `moveBackward()`, `stop()` 호출 시 `facingRight_ = false`
- `isFacingRight() const` 공개 접근자 추가

### 2.4 RVCSimulator (변경)

멤버 선언 순서 변경 — `SimFrontSensor`가 `SimMovementUnit` 참조를 가지므로 초기화 순서 보장:

```cpp
// v2 선언 순서
SimMovementUnit   movementUnit_;          // [위치 변경] 맨 앞으로 이동
SimFrontSensor    frontSensor_{0, movementUnit_};  // [변경] SimFrontSensor 사용
SimObstacleSensor leftSensor_{LEFT, 1};
// [삭제] SimObstacleSensor rightSensor_{RIGHT, 2}
SimDustSensor     dustSensor_;
SimCleaningUnit   cleaningUnit_;
Controller        controller_;  // 5-param 버전 사용
RVC               rvc_;
```

---

## 3. v2 avoidFrontObstacle() 흐름도

```
avoidFrontObstacle()
        │
    stop()
        │
  checkSideObstacles()
  leftSensor→readData()
  leftObstacle_ = isObstacleDetected()
        │
   leftObstacle_?
   ┌────┴────┐
  No        Yes
   │         │
turn(LEFT)  turn(RIGHT)         ← [추가] 우측 확인을 위한 회전
   │         │
forward()  frontSensor→readData()
           frontSensor→isObstacleDetected()
                    │
              blocked?
           ┌────────┴────────┐
          No                Yes
           │                 │
       forward()    avoidAllDirectionObstacle()
```

---

## 4. v2 avoidAllDirectionObstacle() 흐름도

```
avoidAllDirectionObstacle()
        │
    stop()
        │
  moveBackward()
        │
  checkSideObstacles()          ← [추가] 후진 후 좌측 재판단
  leftObstacle_ = isObstacleDetected()
        │
   leftObstacle_?
   ┌────┴────┐
  No        Yes
   │         │
turn(LEFT)  turn(RIGHT)         ← [변경] turn(BACK) 제거
   │         │
   └────┬────┘
     forward()
```

---

## 5. 테스트 변경 사항

### 5.1 단위 테스트 (ControllerTest.cpp)

| 테스트 ID | 변경 구분 | 변경 내용 |
|-----------|-----------|-----------|
| TC-UC02-002 | [변경] | `SensorPosition::RIGHT` 제거 → FRONT/LEFT 두 값만 테스트 |
| TC-UC03-002 | [변경] | left blocked → `turn(RIGHT)` + front sensor 재조회로 우측 확인 |
| TC-UC03-003 | [변경] | `frontSensor_.isObstacleDetected()` 2회 호출로 전방향 막힘 판단 |
| TC-UC03-004 | [변경] | 좌측 센서만 직접 조회 검증 (rightSensor 제거) |
| TC-UC04-002 | [삭제] | `reportObstacle(RIGHT)` — enum 값 없음 |
| TC-UC04-002(new) | [변경] | 좌측 장애물 기록 → 방향 결정에 영향 검증 |
| TC-UC05-001 | [변경] | v2 시퀀스: turn이 backward 전후 발생 → InSequence → 개별 Times 검증으로 변경 |
| TC-UC05-002 | [변경] | 동일 |

### 5.2 통합 테스트 (RVCSystemTest.cpp)

| 시나리오 | 변경 구분 | 변경 내용 |
|---------|-----------|-----------|
| Scenario2 | [변경] | `rightSensor_` 제거; front blocked + left free → turn LEFT |
| Scenario3 | [변경] | front sensor 2회 조회로 전방향 막힘 판단; InSequence → 개별 Times |

### 5.3 시스템 테스트 (RVCSimulatorTest.cpp)

| 시나리오 | 변경 구분 | 변경 내용 |
|---------|-----------|-----------|
| `runClearTicks()` | [변경] | `setRightObstacle(false)` → `setRightSideObstacle(false)` |
| Scenario2 Phase 4 | [변경] | `setRightObstacle(false)` → `setRightSideObstacle(false)` |
| Scenario3 | [변경] | `setRightObstacle(true)` → `setRightSideObstacle(true)`; `getLastTurn() == BACK` → `== RIGHT` |
| Scenario7 Phase 2 | [변경] | `setRightObstacle(true)` → `setRightSideObstacle(true)` |
| Scenario7 Phase 6 | [변경] | `setRightObstacle(true)` → `setRightSideObstacle(true)`; `BACK` → `RIGHT` |

### 5.4 테스트 수

| 계층 | v1 | v2 | 비고 |
|------|----|----|------|
| 단위 (Unit) | 38 | **37** | TC-UC04-002 삭제 |
| 통합 (Integration) | 6 | 6 | 내용 변경, 수 동일 |
| 시스템 (System) | 7 | 7 | 내용 변경, 수 동일 |
| **합계** | **51** | **50** | |

---

## 6. CI/CD 파이프라인

v1과 동일한 GitHub Actions 기반 파이프라인 유지:
- `ci.yml`: CMake 빌드 + ctest 50개 자동 실행
- `analysis.yml`: clang-tidy + cppcheck 정적 분석
- 트리거: push/PR → `main`, `develop` 브랜치
